package p.ithorns.tools.log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.lang.reflect.Method;

/**
 * BizLogAspect
 *
 * 业务日志
 *
 * @author ithorns
 * @version 1.0
 * @date 2021/3/15 14:37
 */
@Order(20)
@Aspect
@Component
@ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint")
public class BizLogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(BizLogAspect.class);

    @Pointcut("execution(* p.ithorns.integration..service..*.*(..)) " +
            "&& @annotation(p.ithorns.tools.log.BizLogSign)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String methodName = point.getTarget().getClass().getName() + "." + signature.getName() + "()";
        String args = JsonUtil.toJson(point.getArgs());
        String sign = method.getAnnotation(BizLogSign.class).value();

        String uuid =  UUIDUtil.uuid(methodName + args);

        Object result = null;
        final StopWatch watch = new StopWatch();
        try {
            // ↵☇
            LOGGER.info("BizLog({})<S>:[{}] |==> {} ↙\r\n↘ <args: {}>", uuid, sign, methodName, args);

            watch.start();
            // 执行方法
            result = point.proceed();
            watch.stop();

            long elapsed = watch.getTotalTimeMillis();

            LOGGER.info("BizLog({})<E>:[{}] |==> elapsed: [{}]ms", uuid, sign, elapsed);

            // 保存业务日志
            // saveBizLog(methodName, args, sign, watch.getTotalTimeMillis());

        } catch (Exception e) {
            if (watch.isRunning()) {
                watch.stop();
            }

            long elapsed2 = watch.getTotalTimeMillis();
            LOGGER.info("BizLog({})<EX>:[{}] |==> elapsed: [{}]ms, {}", uuid, sign, elapsed2, e.getMessage());

            // 保存异常日志
            // saveExceptionLog(methodName, args, sign, elapsed2, e);

            // 注意 被@Transcation注解的Service，事务会失效
            // 业务异常时必须抛出，否则会影响下游操作
            throw e;
        }
        return result;
    }

    /**
     * 业务日志
     *
     * @param methodName -方法(全限定名)
     * @param args       -参数
     * @param sign       -注解属性
     * @param elapsed    -消耗时长
     */
    private void saveBizLog(String methodName, String args, String sign, long elapsed) {

        final BizLog.BizLogBuilder builder = BizLog.builder()
                .method(methodName)
                .params(args)
                .result("RET_SUCCESS")
                .elapsed(elapsed);

        builder.operation(sign);

//        Optional.ofNullable(UserContext.getUser())
//                .ifPresent(user -> builder.username(user.getUserName()));

    }

    /**
     * 业务异常日志
     *
     * @param methodName -方法(全限定名)
     * @param args       -参数
     * @param sign       -注解属性
     * @param elapsed    -消耗时长
     * @param e          -异常
     */
    private void saveExceptionLog(String methodName, String args, String sign, long elapsed, Exception e) {
        final BizLog.BizLogBuilder builder = BizLog.builder();

        builder.method(methodName)
                .params(args)
                .result("FAILURE")
                .msg(e.getMessage())
                .elapsed(elapsed);

        builder.operation(sign);

//        Optional.ofNullable(UserContext.getUser())
//                .ifPresent(user -> builder.username(user.getUserName()));

    }

}
